home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectPlay / ChatPeer / chatpeer.cpp next >
Encoding:
C/C++ Source or Header  |  2001-10-31  |  30.7 KB  |  773 lines

  1. //----------------------------------------------------------------------------
  2. // File: ChatPeer.cpp
  3. //
  4. // Desc: The main game file for the ChatPeer sample.  It connects 
  5. //       players together with two dialog boxes to prompt users on the 
  6. //       connection settings to join or create a session. After the user 
  7. //       connects to a sesssion, the sample displays a multiplayer stage. 
  8. // 
  9. //       After a new game has started the sample begins a very simplistic 
  10. //       chat session where users can send text to each other.
  11. //
  12. // Copyright (c) 1999-2001 Microsoft Corp. All rights reserved.
  13. //-----------------------------------------------------------------------------
  14. #define STRICT
  15. #include <windows.h>
  16. #include <basetsd.h>
  17. #include <richedit.h>
  18. #include <dplay8.h>
  19. #include <dplobby8.h>
  20. #include <dxerr8.h>
  21. #include "NetConnect.h"
  22. #include "DXUtil.h"
  23. #include "resource.h"
  24.  
  25.  
  26.  
  27.  
  28. //-----------------------------------------------------------------------------
  29. // Player context locking defines
  30. //-----------------------------------------------------------------------------
  31. CRITICAL_SECTION g_csPlayerContext;
  32. #define PLAYER_LOCK()                   EnterCriticalSection( &g_csPlayerContext ); 
  33. #define PLAYER_ADDREF( pPlayerInfo )    if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  34. #define PLAYER_RELEASE( pPlayerInfo )   if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) SAFE_DELETE( pPlayerInfo ); } pPlayerInfo = NULL;
  35. #define PLAYER_UNLOCK()                 LeaveCriticalSection( &g_csPlayerContext );
  36.  
  37.  
  38. //-----------------------------------------------------------------------------
  39. // Defines, and constants
  40. //-----------------------------------------------------------------------------
  41. #define DPLAY_SAMPLE_KEY        TEXT("Software\\Microsoft\\DirectX DirectPlay Samples")
  42. #define MAX_PLAYER_NAME         14
  43. #define MAX_CHAT_STRINGS        50
  44. #define MAX_CHAT_STRING_LENGTH  508
  45. #define WM_APP_UPDATE_STATS     (WM_APP + 0)
  46. #define WM_APP_CHAT             (WM_APP + 1)
  47.  
  48.  
  49. // This GUID allows DirectPlay to find other instances of the same game on
  50. // the network.  So it must be unique for every game, and the same for 
  51. // every instance of that game.  // {876A3036-FFD7-46bc-9209-B42F617B9BE7}
  52. GUID g_guidApp = { 0x876a3036, 0xffd7, 0x46bc, { 0x92, 0x9, 0xb4, 0x2f, 0x61, 0x7b, 0x9b, 0xe7 } };
  53.  
  54. struct APP_PLAYER_INFO
  55. {
  56.     LONG  lRefCount;                        // Ref count so we can cleanup when all threads 
  57.                                             // are done w/ this object
  58.     DPNID dpnidPlayer;                      // DPNID of player
  59.     TCHAR strPlayerName[MAX_PLAYER_NAME];   // Player name
  60. };
  61.  
  62.  
  63.  
  64. //-----------------------------------------------------------------------------
  65. // Global variables
  66. //-----------------------------------------------------------------------------
  67. IDirectPlay8Peer*  g_pDP                         = NULL;    // DirectPlay peer object
  68. CNetConnectWizard* g_pNetConnectWizard           = NULL;    // Connection wizard
  69. IDirectPlay8LobbiedApplication* g_pLobbiedApp    = NULL;    // DirectPlay lobbied app 
  70. BOOL               g_bWasLobbyLaunched           = FALSE;   // TRUE if lobby launched
  71. HINSTANCE          g_hInst                       = NULL;    // HINST of app
  72. HWND               g_hDlg                        = NULL;    // HWND of main dialog
  73. DPNID              g_dpnidLocalPlayer            = 0;       // DPNID of local player
  74. LONG               g_lNumberOfActivePlayers      = 0;       // Number of players currently in game
  75. TCHAR              g_strAppName[256]             = TEXT("ChatPeer");
  76. HRESULT            g_hrDialog;                              // Exit code for app 
  77. TCHAR              g_strLocalPlayerName[MAX_PATH];          // Local player name
  78. TCHAR              g_strSessionName[MAX_PATH];              // Session name
  79. TCHAR              g_strPreferredProvider[MAX_PATH];        // Provider string
  80.  
  81.  
  82.  
  83.  
  84. //-----------------------------------------------------------------------------
  85. // App specific DirectPlay messages and structures 
  86. //-----------------------------------------------------------------------------
  87. #define GAME_MSGID_CHAT    1
  88.  
  89. // Change compiler pack alignment to be BYTE aligned, and pop the current value
  90. #pragma pack( push, 1 )
  91.  
  92. struct GAMEMSG_GENERIC
  93. {
  94.     BYTE nType;
  95. };
  96.  
  97. struct GAMEMSG_CHAT : public GAMEMSG_GENERIC
  98. {
  99.     TCHAR strChatString[MAX_CHAT_STRING_LENGTH];
  100. };
  101.  
  102. // Pop the old pack alignment
  103. #pragma pack( pop )
  104.  
  105.  
  106. struct APP_QUEUED_DATA 
  107. {
  108.     GAMEMSG_CHAT* pChatMsg;
  109.     DPNHANDLE hBufferHandle;
  110. };
  111.  
  112.  
  113.  
  114. //-----------------------------------------------------------------------------
  115. // Function-prototypes
  116. //-----------------------------------------------------------------------------
  117. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  118. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  119. INT_PTR CALLBACK ChatDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam );
  120. HRESULT        InitDirectPlay();
  121. HRESULT        OnInitDialog( HWND hDlg );
  122. HRESULT        SendChatMessage( HWND hDlg );
  123.  
  124.  
  125.  
  126.  
  127. //-----------------------------------------------------------------------------
  128. // Name: WinMain()
  129. // Desc: Entry point for the application.  Since we use a simple dialog for 
  130. //       user interaction we don't need to pump messages.
  131. //-----------------------------------------------------------------------------
  132. INT APIENTRY WinMain( HINSTANCE hInst, HINSTANCE hPrevInst, 
  133.                       LPSTR pCmdLine, INT nCmdShow )
  134. {
  135.     HRESULT hr;
  136.     HKEY    hDPlaySampleRegKey;
  137.     BOOL    bConnectSuccess = FALSE;
  138.  
  139.     g_hInst = hInst;
  140.     InitializeCriticalSection( &g_csPlayerContext );
  141.  
  142.     // Read persistent state information from registry
  143.     RegCreateKeyEx( HKEY_CURRENT_USER, DPLAY_SAMPLE_KEY, 0, NULL,
  144.                     REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, 
  145.                     &hDPlaySampleRegKey, NULL );
  146.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), 
  147.                              g_strLocalPlayerName, MAX_PATH, TEXT("TestPlayer") );
  148.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), 
  149.                              g_strSessionName, MAX_PATH, TEXT("TestGame") );
  150.     DXUtil_ReadStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), 
  151.                              g_strPreferredProvider, MAX_PATH, TEXT("DirectPlay8 TCP/IP Service Provider") );
  152.  
  153.     // Init COM so we can use CoCreateInstance
  154.     CoInitializeEx( NULL, COINIT_MULTITHREADED );
  155.  
  156.     // Create helper class
  157.     g_pNetConnectWizard = new CNetConnectWizard( hInst, NULL, g_strAppName, &g_guidApp );
  158.  
  159.     if( FAILED( hr = InitDirectPlay() ) )
  160.     {
  161.         DXTRACE_ERR( TEXT("InitDirectPlay"), hr );
  162.         MessageBox( NULL, TEXT("Failed initializing IDirectPlay8Peer. ")
  163.                     TEXT("The sample will now quit."),
  164.                     TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  165.         goto LCleanup;
  166.     }
  167.  
  168.     // Check if we were launched from a lobby client
  169.     if( g_bWasLobbyLaunched && g_pNetConnectWizard->HaveConnectionSettingsFromLobby() )
  170.     {
  171.         // If were lobby launched then DPL_MSGID_CONNECT has already been
  172.         // handled, so we can just tell the wizard to connect to the lobby
  173.         // that has sent us a DPL_MSGID_CONNECT msg.
  174.         if( FAILED( hr = g_pNetConnectWizard->ConnectUsingLobbySettings() ) )
  175.         {
  176.             DXTRACE_ERR( TEXT("ConnectUsingLobbySettings"), hr );
  177.             MessageBox( NULL, TEXT("Failed to connect using lobby settings. ")
  178.                         TEXT("The sample will now quit."),
  179.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  180.  
  181.             bConnectSuccess = FALSE;
  182.         }
  183.         else
  184.         {
  185.             // Read information from g_pNetConnectWizard
  186.             _tcscpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName() );
  187.  
  188.             bConnectSuccess = TRUE; 
  189.         }
  190.     }
  191.     else
  192.     {
  193.         // If not lobby launched, prompt the user about the network 
  194.         // connection and which session they would like to join or 
  195.         // if they want to create a new one.
  196.  
  197.         // Setup connection wizard
  198.         g_pNetConnectWizard->SetPlayerName( g_strLocalPlayerName );
  199.         g_pNetConnectWizard->SetSessionName( g_strSessionName );
  200.         g_pNetConnectWizard->SetPreferredProvider( g_strPreferredProvider );
  201.  
  202.         // Do the connection wizard
  203.         hr = g_pNetConnectWizard->DoConnectWizard( FALSE );        
  204.         if( FAILED( hr ) ) 
  205.         {
  206.             DXTRACE_ERR( TEXT("DoConnectWizard"), hr );
  207.             MessageBox( NULL, TEXT("Multiplayer connect failed. ")
  208.                         TEXT("The sample will now quit."),
  209.                         TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  210.             bConnectSuccess = FALSE;
  211.         } 
  212.         else if( hr == NCW_S_QUIT ) 
  213.         {
  214.             // The user canceled the Multiplayer connect, so quit 
  215.             bConnectSuccess = FALSE;
  216.         }
  217.         else
  218.         {
  219.             bConnectSuccess = TRUE; 
  220.  
  221.             // Read information from g_pNetConnectWizard
  222.             _tcscpy( g_strLocalPlayerName, g_pNetConnectWizard->GetPlayerName() );
  223.             _tcscpy( g_strSessionName, g_pNetConnectWizard->GetSessionName() );
  224.             _tcscpy( g_strPreferredProvider, g_pNetConnectWizard->GetPreferredProvider() );
  225.  
  226.             // Write information to the registry
  227.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Player Name"), g_strLocalPlayerName );
  228.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Session Name"), g_strSessionName );
  229.             DXUtil_WriteStringRegKey( hDPlaySampleRegKey, TEXT("Preferred Provider"), g_strPreferredProvider );
  230.         }
  231.     }
  232.  
  233.     if( bConnectSuccess )
  234.     {
  235.         // App is now connected via DirectPlay, so start the game.  
  236.  
  237.         // For this sample, we just start a simple dialog box game.
  238.         g_hrDialog = S_OK;
  239.         DialogBox( hInst, MAKEINTRESOURCE(IDD_MAIN_GAME), NULL, (DLGPROC) ChatDlgProc );
  240.  
  241.         if( FAILED( g_hrDialog ) )
  242.         {
  243.             if( g_hrDialog == DPNERR_CONNECTIONLOST )
  244.             {
  245.                 MessageBox( NULL, TEXT("The DirectPlay session was lost. ")
  246.                             TEXT("The sample will now quit."),
  247.                             TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  248.             }
  249.             else
  250.             {
  251.                 DXTRACE_ERR( TEXT("DialogBox"), g_hrDialog );
  252.                 MessageBox( NULL, TEXT("An error occured during the game. ")
  253.                             TEXT("The sample will now quit."),
  254.                             TEXT("DirectPlay Sample"), MB_OK | MB_ICONERROR );
  255.             }
  256.         }
  257.     }
  258.  
  259. LCleanup:
  260.     // Cleanup DirectPlay and helper classes
  261.     g_pNetConnectWizard->Shutdown();
  262.  
  263.     if( g_pDP )
  264.     {
  265.         g_pDP->Close(0);
  266.         SAFE_RELEASE( g_pDP );
  267.     }
  268.  
  269.     if( g_pLobbiedApp )
  270.     {
  271.         g_pLobbiedApp->Close( 0 );
  272.         SAFE_RELEASE( g_pLobbiedApp );
  273.     }    
  274.  
  275.     // Don't delete the wizard until we know that 
  276.     // DirectPlay is out of its message handlers.
  277.     // This will be true after Close() has been called. 
  278.     SAFE_DELETE( g_pNetConnectWizard );
  279.  
  280.     RegCloseKey( hDPlaySampleRegKey );
  281.     DeleteCriticalSection( &g_csPlayerContext );
  282.     CoUninitialize();
  283.  
  284.     return TRUE;
  285. }
  286.  
  287.  
  288.  
  289.  
  290. //-----------------------------------------------------------------------------
  291. // Name: InitDirectPlay()
  292. // Desc: 
  293. //-----------------------------------------------------------------------------
  294. HRESULT InitDirectPlay()
  295. {
  296.     DPNHANDLE hLobbyLaunchedConnection = NULL;
  297.     HRESULT hr;
  298.  
  299.     // Create IDirectPlay8Peer
  300.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL, 
  301.                                        CLSCTX_INPROC_SERVER,
  302.                                        IID_IDirectPlay8Peer, 
  303.                                        (LPVOID*) &g_pDP ) ) )
  304.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  305.  
  306.     // Create IDirectPlay8LobbiedApplication
  307.     if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8LobbiedApplication, NULL, 
  308.                                        CLSCTX_INPROC_SERVER,
  309.                                        IID_IDirectPlay8LobbiedApplication, 
  310.                                        (LPVOID*) &g_pLobbiedApp ) ) )
  311.         return DXTRACE_ERR( TEXT("CoCreateInstance"), hr );
  312.  
  313.     // Init the helper class, now that g_pDP and g_pLobbiedApp are valid
  314.     g_pNetConnectWizard->Init( g_pDP, g_pLobbiedApp );
  315.  
  316.     // Init IDirectPlay8Peer
  317.     if( FAILED( hr = g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  318.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  319.  
  320.     // Init IDirectPlay8LobbiedApplication.  Before this Initialize() returns 
  321.     // a DPL_MSGID_CONNECT msg may come in to the DirectPlayLobbyMessageHandler 
  322.     // so be prepared ahead of time.
  323.     if( FAILED( hr = g_pLobbiedApp->Initialize( NULL, DirectPlayLobbyMessageHandler, 
  324.                                                 &hLobbyLaunchedConnection, 0 ) ) )
  325.         return DXTRACE_ERR( TEXT("Initialize"), hr );
  326.  
  327.     // IDirectPlay8LobbiedApplication::Initialize returns a handle to a connnection
  328.     // if we have been lobby launced.  Initialize is guanteeded to return after 
  329.     // the DPL_MSGID_CONNECT msg has been processed.  So unless a we are expected 
  330.     // multiple lobby connections, we do not need to remember the lobby connection
  331.     // handle since it will be recorded upon the DPL_MSGID_CONNECT msg.
  332.     g_bWasLobbyLaunched = ( hLobbyLaunchedConnection != NULL );
  333.  
  334.     return S_OK;
  335. }
  336.  
  337.  
  338.  
  339.  
  340. //-----------------------------------------------------------------------------
  341. // Name: ChatDlgProc()
  342. // Desc: Handles dialog messages
  343. //-----------------------------------------------------------------------------
  344. INT_PTR CALLBACK ChatDlgProc( HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam )
  345. {
  346.     switch( msg ) 
  347.     {
  348.         case WM_INITDIALOG:
  349.         {
  350.             g_hDlg = hDlg;
  351.             if( FAILED( g_hrDialog = OnInitDialog( hDlg ) ) )
  352.             {
  353.                 DXTRACE_ERR( TEXT("OnInitDialog"), g_hrDialog );
  354.                 EndDialog( hDlg, 0 );
  355.             }
  356.             break;
  357.         }
  358.  
  359.         case WM_APP_UPDATE_STATS:
  360.         {
  361.             // Update the number of players in the game
  362.             TCHAR strNumberPlayers[32];
  363.  
  364.             wsprintf( strNumberPlayers, TEXT("%d"), g_lNumberOfActivePlayers );
  365.             SetDlgItemText( hDlg, IDC_NUM_PLAYERS, strNumberPlayers );
  366.             break;
  367.         }
  368.  
  369.         case WM_APP_CHAT:
  370.         {
  371.             HRESULT          hr;
  372.             DPNID            dpnidPlayer = (DPNID) wParam;
  373.             APP_PLAYER_INFO* pPlayerInfo = NULL;
  374.  
  375.             PLAYER_LOCK(); // enter player context CS
  376.  
  377.             // Get the player context accosicated with this DPNID
  378.             hr = g_pDP->GetPlayerContext( dpnidPlayer, 
  379.                                           (LPVOID* const) &pPlayerInfo,
  380.                                           0);
  381.  
  382.             PLAYER_ADDREF( pPlayerInfo ); // addref player, since we are using it now
  383.             PLAYER_UNLOCK(); // leave player context CS
  384.  
  385.             APP_QUEUED_DATA* pQueuedData = (APP_QUEUED_DATA*) lParam;
  386.  
  387.             if( FAILED(hr) || pPlayerInfo == NULL )
  388.             {
  389.                 // The player who sent this may have gone away before this 
  390.                 // message was handled, so just ignore it
  391.                 if( pQueuedData )
  392.                 {
  393.                      g_pDP->ReturnBuffer( pQueuedData->hBufferHandle,0 );
  394.                      SAFE_DELETE( pQueuedData );
  395.                 }
  396.                 break;
  397.             }
  398.             
  399.             // Add the message to the local listbox
  400.             HWND hWndChatBox = GetDlgItem( hDlg, IDC_CHAT_LISTBOX );
  401.             int nCount = (int)SendMessage( hWndChatBox, LB_GETCOUNT, 0, 0 );
  402.             if( nCount > MAX_CHAT_STRINGS )
  403.                 SendMessage( hWndChatBox, LB_DELETESTRING, 0, 0 );
  404.  
  405.             // Make the chat string from the player's name and the edit box string
  406.             TCHAR strChatBuffer[MAX_PLAYER_NAME + MAX_CHAT_STRING_LENGTH + 32];
  407.             wsprintf( strChatBuffer, TEXT("<%s> %s"), pPlayerInfo->strPlayerName, pQueuedData->pChatMsg->strChatString );
  408.  
  409.             PLAYER_LOCK(); // enter player context CS
  410.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  411.             PLAYER_UNLOCK(); // leave player context CS            
  412.  
  413.             // Add it, and make sure it is visible
  414.             int nIndex = (int)SendMessage( hWndChatBox, LB_ADDSTRING, 0, (LPARAM)strChatBuffer );
  415.             SendMessage( hWndChatBox, LB_SETTOPINDEX, nIndex, 0 );
  416.  
  417.             // Done with the buffer, so return it DirectPlay, 
  418.             // so that the memory can be reused
  419.             g_pDP->ReturnBuffer( pQueuedData->hBufferHandle,0 );
  420.             SAFE_DELETE( pQueuedData );
  421.             break;
  422.         }
  423.  
  424.         case WM_COMMAND:
  425.             switch( LOWORD(wParam) )
  426.             {
  427.                 case IDC_CHAT_EDIT:
  428.                     if( HIWORD(wParam) == EN_UPDATE )
  429.                     {
  430.                         BOOL bEnableSend;
  431.                         if( 0 == GetWindowTextLength( GetDlgItem( hDlg, IDC_CHAT_EDIT ) ) )
  432.                             bEnableSend = FALSE;
  433.                         else
  434.                             bEnableSend = TRUE;
  435.  
  436.                         EnableWindow( GetDlgItem( hDlg, IDC_SEND ), bEnableSend );
  437.                     }
  438.                     break;
  439.  
  440.                 case IDC_SEND:
  441.                     // The enter key was pressed, so send out the chat message
  442.                     if( FAILED( g_hrDialog = SendChatMessage( hDlg ) ) )
  443.                     {
  444.                         DXTRACE_ERR( TEXT("SendChatMessage"), g_hrDialog );
  445.                         EndDialog( hDlg, 0 );
  446.                     }
  447.                     break;
  448.  
  449.                 case IDCANCEL:
  450.                     g_hrDialog = S_OK;
  451.                     EndDialog( hDlg, 0 );
  452.                     return TRUE;
  453.             }
  454.             break;
  455.     }
  456.  
  457.     return FALSE; // Didn't handle message
  458. }
  459.  
  460.  
  461.  
  462.  
  463. //-----------------------------------------------------------------------------
  464. // Name: OnInitDialog()
  465. // Desc: Inits the dialog for the chat client.
  466. //-----------------------------------------------------------------------------
  467. HRESULT OnInitDialog( HWND hDlg )
  468. {
  469.     // Load and set the icon
  470.     HICON hIcon = LoadIcon( g_hInst, MAKEINTRESOURCE( IDI_MAIN ) );
  471.     SendMessage( hDlg, WM_SETICON, ICON_BIG,   (LPARAM) hIcon );  // Set big icon
  472.     SendMessage( hDlg, WM_SETICON, ICON_SMALL, (LPARAM) hIcon );  // Set small icon
  473.  
  474.     // Display local player's name
  475.     SetDlgItemText( hDlg, IDC_PLAYER_NAME, g_strLocalPlayerName );
  476.  
  477.     PostMessage( hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  478.     SetFocus( GetDlgItem( hDlg, IDC_CHAT_EDIT ) );
  479.  
  480.     SendMessage( GetDlgItem( hDlg, IDC_CHAT_EDIT ), EM_SETEVENTMASK, 0, ENM_UPDATE );
  481.     EnableWindow( GetDlgItem( hDlg, IDC_SEND ), FALSE );
  482.  
  483.     if( g_pNetConnectWizard->IsHostPlayer() )
  484.         SetWindowText( hDlg, TEXT("ChatPeer (Host)") );
  485.     else
  486.         SetWindowText( hDlg, TEXT("ChatPeer") );
  487.  
  488.     return S_OK;
  489. }
  490.  
  491.  
  492.  
  493.  
  494. //-----------------------------------------------------------------------------
  495. // Name: DirectPlayMessageHandler
  496. // Desc: Handler for DirectPlay messages.  This function is called by
  497. //       the DirectPlay message handler pool of threads, so be care of thread
  498. //       synchronization problems with shared memory
  499. //-----------------------------------------------------------------------------
  500. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, 
  501.                                          DWORD dwMessageId, 
  502.                                          PVOID pMsgBuffer )
  503. {
  504.     // Try not to stay in this message handler for too long, otherwise
  505.     // there will be a backlog of data.  The best solution is to 
  506.     // queue data as it comes in, and then handle it on other threads
  507.     // as this sample shows
  508.  
  509.     // This function is called by the DirectPlay message handler pool of 
  510.     // threads, so be care of thread synchronization problems with shared memory
  511.  
  512.     HRESULT hReturn = S_OK;
  513.  
  514.     switch( dwMessageId )
  515.     {
  516.         case DPN_MSGID_CREATE_PLAYER:
  517.         {
  518.             HRESULT hr;
  519.             PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
  520.             pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER)pMsgBuffer;
  521.  
  522.             // Create a new and fill in a APP_PLAYER_INFO
  523.             APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  524.             ZeroMemory( pPlayerInfo, sizeof(APP_PLAYER_INFO) );
  525.             pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
  526.             pPlayerInfo->lRefCount   = 1;
  527.  
  528.             // Get the peer info and extract its name
  529.             DWORD dwSize = 0;
  530.             DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
  531.             hr = DPNERR_CONNECTING;
  532.             
  533.             // GetPeerInfo might return DPNERR_CONNECTING when connecting, 
  534.             // so just keep calling it if it does
  535.             while( hr == DPNERR_CONNECTING ) 
  536.                 hr = g_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );                                
  537.                 
  538.             if( hr == DPNERR_BUFFERTOOSMALL )
  539.             {
  540.                 pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
  541.                 ZeroMemory( pdpPlayerInfo, dwSize );
  542.                 pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
  543.                 
  544.                 hr = g_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
  545.                 if( SUCCEEDED(hr) )
  546.                 {
  547.                     // This stores a extra TCHAR copy of the player name for 
  548.                     // easier access.  This will be redundent copy since DPlay 
  549.                     // also keeps a copy of the player name in GetPeerInfo()
  550.                     DXUtil_ConvertWideStringToGeneric( pPlayerInfo->strPlayerName, 
  551.                                                        pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );    
  552.                                                        
  553.                     if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL )
  554.                         g_dpnidLocalPlayer = pCreatePlayerMsg->dpnidPlayer;
  555.                 }
  556.  
  557.                 SAFE_DELETE_ARRAY( pdpPlayerInfo );
  558.             }
  559.                 
  560.             // Tell DirectPlay to store this pPlayerInfo 
  561.             // pointer in the pvPlayerContext.
  562.             pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
  563.  
  564.             // Update the number of active players, and 
  565.             // post a message to the dialog thread to update the 
  566.             // UI.  This keeps the DirectPlay message handler 
  567.             // from blocking
  568.             InterlockedIncrement( &g_lNumberOfActivePlayers );
  569.             if( g_hDlg != NULL )
  570.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  571.  
  572.             break;
  573.         }
  574.  
  575.         case DPN_MSGID_DESTROY_PLAYER:
  576.         {
  577.             PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
  578.             pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
  579.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
  580.  
  581.             PLAYER_LOCK();                  // enter player context CS
  582.             PLAYER_RELEASE( pPlayerInfo );  // Release player and cleanup if needed
  583.             PLAYER_UNLOCK();                // leave player context CS
  584.  
  585.             // Update the number of active players, and 
  586.             // post a message to the dialog thread to update the 
  587.             // UI.  This keeps the DirectPlay message handler 
  588.             // from blocking
  589.             InterlockedDecrement( &g_lNumberOfActivePlayers );
  590.             if( g_hDlg != NULL )
  591.                 PostMessage( g_hDlg, WM_APP_UPDATE_STATS, 0, 0 );
  592.  
  593.             break;
  594.         }
  595.  
  596.         case DPN_MSGID_HOST_MIGRATE:
  597.         {
  598.             PDPNMSG_HOST_MIGRATE pHostMigrateMsg;
  599.             pHostMigrateMsg = (PDPNMSG_HOST_MIGRATE)pMsgBuffer;
  600.  
  601.             if( pHostMigrateMsg->dpnidNewHost == g_dpnidLocalPlayer )
  602.                 SetWindowText( g_hDlg, TEXT("ChatPeer (Host)") );
  603.             break;
  604.         }
  605.  
  606.         case DPN_MSGID_TERMINATE_SESSION:
  607.         {
  608.             PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  609.             pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  610.  
  611.             g_hrDialog = DPNERR_CONNECTIONLOST;
  612.             EndDialog( g_hDlg, 0 );
  613.             break;
  614.         }
  615.  
  616.         case DPN_MSGID_RECEIVE:
  617.         {
  618.             PDPNMSG_RECEIVE pReceiveMsg;
  619.             pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  620.             APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;           
  621.             if( NULL == pPlayerInfo )
  622.                 break;
  623.  
  624.             GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  625.             if( pMsg->nType == GAME_MSGID_CHAT )
  626.             {
  627.                 // This message is sent when a player has send a chat message to us, so 
  628.                 // post a message to the dialog thread to update the UI.  
  629.                 // This keeps the DirectPlay threads from blocking, and also
  630.                 // serializes the recieves since DirectPlayMessageHandler can
  631.                 // be called simultaneously from a pool of DirectPlay threads.
  632.                 GAMEMSG_CHAT* pChatMessage = (GAMEMSG_CHAT*) pMsg;
  633.  
  634.                 // Record the buffer handle so the buffer can be returned later 
  635.                 APP_QUEUED_DATA* pQueuedData = new APP_QUEUED_DATA;
  636.                 pQueuedData->hBufferHandle = pReceiveMsg->hBufferHandle;
  637.                 pQueuedData->pChatMsg      = pChatMessage;
  638.  
  639.                 // Pass the APP_QUEUED_DATA to the main dialog thread, so it can
  640.                 // process it.  It will also cleanup the struct
  641.                 PostMessage( g_hDlg, WM_APP_CHAT, 
  642.                              pPlayerInfo->dpnidPlayer, (LPARAM) pQueuedData );
  643.  
  644.                 // Tell DirectPlay to assume that ownership of the buffer 
  645.                 // has been transferred to the application, and so it will 
  646.                 // neither free nor modify it until ownership is returned 
  647.                 // to DirectPlay through the ReturnBuffer() call.
  648.                 hReturn = DPNSUCCESS_PENDING;
  649.             }
  650.             break;
  651.         }
  652.     }
  653.  
  654.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  655.     // so it can be informed of messages such as DPN_MSGID_ENUM_HOSTS_RESPONSE.
  656.     if( hReturn != DPNSUCCESS_PENDING && SUCCEEDED(hReturn) && g_pNetConnectWizard )
  657.         hReturn = g_pNetConnectWizard->MessageHandler( pvUserContext, dwMessageId, pMsgBuffer );
  658.     
  659.     return hReturn;
  660. }
  661.  
  662.  
  663.  
  664.  
  665. //-----------------------------------------------------------------------------
  666. // Name: DirectPlayLobbyMessageHandler
  667. // Desc: Handler for DirectPlay lobby messages.  This function is called by
  668. //       the DirectPlay lobby message handler pool of threads, so be careful of 
  669. //       thread synchronization problems with shared memory
  670. //-----------------------------------------------------------------------------
  671. HRESULT WINAPI DirectPlayLobbyMessageHandler( PVOID pvUserContext, 
  672.                                               DWORD dwMessageId, 
  673.                                               PVOID pMsgBuffer )
  674. {
  675.     switch( dwMessageId )
  676.     {
  677.         case DPL_MSGID_CONNECT:
  678.         {
  679.             PDPL_MESSAGE_CONNECT pConnectMsg;
  680.             pConnectMsg = (PDPL_MESSAGE_CONNECT)pMsgBuffer;
  681.  
  682.             // The CNetConnectWizard will handle this message for us,
  683.             // so there is nothing we need to do here for this simple
  684.             // sample.
  685.             break;
  686.         }
  687.  
  688.         case DPL_MSGID_DISCONNECT:
  689.         {
  690.             PDPL_MESSAGE_DISCONNECT pDisconnectMsg;
  691.             pDisconnectMsg = (PDPL_MESSAGE_DISCONNECT)pMsgBuffer;
  692.  
  693.             // We should free any data associated with the lobby 
  694.             // client here, but there is none.
  695.             break;
  696.         }
  697.  
  698.         case DPL_MSGID_RECEIVE:
  699.         {
  700.             PDPL_MESSAGE_RECEIVE pReceiveMsg;
  701.             pReceiveMsg = (PDPL_MESSAGE_RECEIVE)pMsgBuffer;
  702.  
  703.             // The lobby client sent us data.  This sample doesn't
  704.             // expected data from the client, but it is useful 
  705.             // for more complex apps.
  706.             break;
  707.         }
  708.  
  709.         case DPL_MSGID_CONNECTION_SETTINGS:
  710.         {
  711.             PDPL_MESSAGE_CONNECTION_SETTINGS pConnectionStatusMsg;
  712.             pConnectionStatusMsg = (PDPL_MESSAGE_CONNECTION_SETTINGS)pMsgBuffer;
  713.  
  714.             // The lobby client has changed the connection settings.  
  715.             // This simple sample doesn't handle this, but more complex apps may
  716.             // want to.
  717.             break;
  718.         }
  719.     }
  720.  
  721.     // Make sure the DirectPlay MessageHandler calls the CNetConnectWizard handler, 
  722.     // so the wizard can be informed of lobby messages such as DPL_MSGID_CONNECT
  723.     if( g_pNetConnectWizard )
  724.         return g_pNetConnectWizard->LobbyMessageHandler( pvUserContext, dwMessageId, 
  725.                                                          pMsgBuffer );
  726.     
  727.     return S_OK;
  728. }
  729.  
  730.  
  731.  
  732.  
  733. //-----------------------------------------------------------------------------
  734. // Name: SendChatMessage()
  735. // Desc: Create chat string based on the editbox and send it to everyone 
  736. //-----------------------------------------------------------------------------
  737. HRESULT SendChatMessage( HWND hDlg )
  738. {
  739.     // Get length of item text
  740.     DWORD dwEditboxBufferSize = (DWORD)SendDlgItemMessage( hDlg, IDC_CHAT_EDIT, 
  741.                                                            WM_GETTEXTLENGTH, 0, 0 );
  742.     if( dwEditboxBufferSize == 0 )
  743.         return S_OK;  // Don't do anything for blank messages 
  744.  
  745.     GAMEMSG_CHAT msgChat;
  746.     msgChat.nType = GAME_MSGID_CHAT;
  747.     GetDlgItemText( hDlg, IDC_CHAT_EDIT, msgChat.strChatString, MAX_CHAT_STRING_LENGTH );
  748.  
  749.     // Set the dwBufferSize to only send the string that has valid text in it.  
  750.     // Otherwise bandwidth is wasted.
  751.     DPN_BUFFER_DESC bufferDesc;
  752.     bufferDesc.dwBufferSize = sizeof(msgChat.nType) + 
  753.                               (_tcslen(msgChat.strChatString)+1)*sizeof(TCHAR);
  754.     bufferDesc.pBufferData  = (BYTE*) &msgChat;
  755.  
  756.     // Send it to all of the players include the local client
  757.     // DirectPlay will tell via the message handler 
  758.     // if there are any severe errors, so ignore any errors 
  759.     DPNHANDLE hAsync;
  760.     g_pDP->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
  761.                    0, NULL, &hAsync, DPNSEND_GUARANTEED );
  762.  
  763.     // Blank out edit box
  764.     SetDlgItemText( hDlg, IDC_CHAT_EDIT, TEXT("") );
  765.  
  766.     return S_OK;
  767. }
  768.  
  769.  
  770.  
  771.  
  772.  
  773.